Atraskite savybėmis grįstą testavimą su Python Hypothesis. Išeikite už pavyzdžiais pagrįstų testų ribų, kad rastumėte kraštutinius atvejus ir kurtumėte patikimesnę programinę įrangą.
Daugiau nei vienetiniai testai: išsami savybėmis grįsto testavimo analizė su Python Hypothesis biblioteka
Programinės įrangos kūrimo pasaulyje testavimas yra kokybės pamatas. Dešimtmečius dominuojanti paradigma buvo testavimas pavyzdžiais. Mes kruopščiai kuriame įvesties duomenis, apibrėžiame laukiamus rezultatus ir rašome tvirtinimus (assertions), kad patikrintume, ar mūsų kodas veikia kaip planuota. Šis požiūris, naudojamas tokiose sistemose kaip unittest
ir pytest
, yra galingas ir būtinas. Bet kas, jei pasakyčiau, kad egzistuoja papildantis požiūris, galintis atskleisti klaidas, apie kurias net nepagalvojote ieškoti?
Sveiki atvykę į savybėmis grįsto testavimo pasaulį – paradigmą, kuri perkelia dėmesį nuo konkrečių pavyzdžių testavimo prie bendrųjų jūsų kodo savybių tikrinimo. O Python ekosistemoje neginčijamas šio požiūrio lyderis yra biblioteka, vadinama Hypothesis.
Šis išsamus vadovas pavers jus iš visiško naujoko į užtikrintą savybėmis grįsto testavimo su Hypothesis praktiką. Išnagrinėsime pagrindines sąvokas, pasinersime į praktinius pavyzdžius ir išmoksime, kaip integruoti šį galingą įrankį į savo kasdienę kūrimo eigą, kad sukurtumėte tvirtesnę, patikimesnę ir atsparesnę klaidoms programinę įrangą.
Kas yra savybėmis grįstas testavimas? Mąstymo pokytis
Norėdami suprasti Hypothesis, pirmiausia turime suvokti pagrindinę savybėmis grįsto testavimo idėją. Palyginkime jį su tradiciniu, mums visiems žinomu testavimu pavyzdžiais.
Testavimas pavyzdžiais: pažįstamas kelias
Įsivaizduokite, kad parašėte individualią rūšiavimo funkciją my_sort()
. Naudojant testavimą pavyzdžiais, jūsų mąstymo procesas būtų toks:
- „Išbandykime su paprastu, surūšiuotu sąrašu.“ ->
assert my_sort([1, 2, 3]) == [1, 2, 3]
- „O kaip su atvirkščiai surūšiuotu sąrašu?“ ->
assert my_sort([3, 2, 1]) == [1, 2, 3]
- „Kaip dėl tuščio sąrašo?“ ->
assert my_sort([]) == []
- „Sąrašas su dublikatais?“ ->
assert my_sort([5, 1, 5, 2]) == [1, 2, 5, 5]
- „Ir sąrašas su neigiamais skaičiais?“ ->
assert my_sort([-1, -5, 0]) == [-5, -1, 0]
Tai veiksminga, bet turi esminį apribojimą: jūs testuojate tik tuos atvejus, kuriuos sugalvojate. Jūsų testai yra tokie geri, kokia gera jūsų vaizduotė. Galite praleisti kraštutinius atvejus, susijusius su labai dideliais skaičiais, slankiojo kablelio netikslumais, specifiniais unikodo simboliais ar sudėtingomis duomenų kombinacijomis, kurios sukelia netikėtą elgesį.
Savybėmis grįstas testavimas: mąstymas invariantais
Savybėmis grįstas testavimas apverčia scenarijų. Užuot teikę konkrečius pavyzdžius, jūs apibrėžiate savo funkcijos savybes, arba invariantus, – taisykles, kurios turi būti teisingos bet kokiems galimiems įvesties duomenims. Mūsų my_sort()
funkcijai šios savybės galėtų būti:
- Išvestis yra surūšiuota: bet kokiam skaičių sąrašui, kiekvienas elementas išvesties sąraše yra mažesnis arba lygus už po jo einantį elementą.
- Išvestyje yra tie patys elementai kaip ir įvestyje: surūšiuotas sąrašas yra tik originalaus sąrašo permutacija; jokie elementai nėra pridedami ar prarandami.
- Funkcija yra idempotentiška: jau surūšiuoto sąrašo rūšiavimas neturėtų jo pakeisti. Tai yra,
my_sort(my_sort(some_list)) == my_sort(some_list)
.
Taikydami šį požiūrį, jūs nerašote testavimo duomenų. Jūs rašote taisykles. Tada leidžiate sistemai, tokiai kaip Hypothesis, sugeneruoti šimtus ar tūkstančius atsitiktinių, įvairių ir dažnai klastingų įvesčių, kad bandytų paneigti jūsų savybes. Jei ji randa įvestį, kuri pažeidžia savybę, ji rado klaidą.
Pristatome Hypothesis: jūsų automatizuotas testavimo duomenų generatorius
Hypothesis yra pagrindinė savybėmis grįsto testavimo biblioteka Python kalbai. Ji paima jūsų apibrėžtas savybes ir atlieka sunkų darbą generuodama testavimo duomenis, kad jas išbandytų. Tai ne tik atsitiktinių duomenų generatorius; tai išmanus ir galingas įrankis, sukurtas efektyviai rasti klaidas.
Pagrindinės Hypothesis savybės
- Automatinis testų atvejų generavimas: Jūs apibrėžiate reikiamų duomenų *formą* (pvz., „sveikųjų skaičių sąrašas“, „eilutė, sudaryta tik iš raidžių“, „ateities data ir laikas“), o Hypothesis sugeneruoja platų pavyzdžių, atitinkančių tą formą, spektrą.
- Išmanusis sutraukimas (angl. Shrinking): Tai magiška funkcija. Kai Hypothesis randa nesėkmingą testo atvejį (pvz., 50 sudėtingų skaičių sąrašą, kuris sugadina jūsų rūšiavimo funkciją), ji ne tik praneša apie tą didžiulį sąrašą. Ji išmaniai ir automatiškai supaprastina įvestį, kad rastų mažiausią įmanomą pavyzdį, kuris vis dar sukelia klaidą. Užuot pranešusi apie 50 elementų sąrašą, ji gali pranešti, kad klaida įvyksta tik su
[inf, nan]
. Tai daro derinimo procesą neįtikėtinai greitą ir efektyvų. - Sklandi integracija: Hypothesis puikiai integruojasi su populiariomis testavimo sistemomis, tokiomis kaip
pytest
irunittest
. Galite pridėti savybėmis grįstus testus šalia esamų pavyzdžiais pagrįstų testų, nekeisdami savo darbo eigos. - Gausi strategijų biblioteka: Ji turi didžiulę integruotų „strategijų“ kolekciją, skirtą generuoti viską nuo paprastų sveikųjų skaičių ir eilučių iki sudėtingų, įdėtų duomenų struktūrų, laiko juostas palaikančių datų ir laikų ir net NumPy masyvų.
- Būsenos testavimas (angl. Stateful Testing): Sudėtingesnėms sistemoms Hypothesis gali testuoti veiksmų sekas, siekiant rasti klaidų būsenų perėjimuose, o tai yra ypač sunku padaryti su pavyzdžiais pagrįstu testavimu.
Darbo pradžia: jūsų pirmasis Hypothesis testas
Išbandykime praktiškai. Geriausias būdas suprasti Hypothesis yra pamatyti jį veikiant.
Diegimas
Pirma, jums reikės įdiegti Hypothesis ir jūsų pasirinktą testų paleidimo įrankį (mes naudosime pytest
). Tai taip paprasta:
pip install pytest hypothesis
Paprastas pavyzdys: absoliučios vertės funkcija
Panagrinėkime paprastą funkciją, kuri turėtų apskaičiuoti skaičiaus absoliučiąją vertę. Šiek tiek klaidingas įgyvendinimas galėtų atrodyti taip:
# faile `my_math.py` def custom_abs(x): """Individualus absoliučios vertės funkcijos įgyvendinimas.""" if x < 0: return -x return x
Dabar parašykime testo failą test_my_math.py
. Pirmiausia, tradicinis pytest
požiūris:
# test_my_math.py (pagrįstas pavyzdžiais) def test_abs_positive(): assert custom_abs(5) == 5 def test_abs_negative(): assert custom_abs(-5) == 5 def test_abs_zero(): assert custom_abs(0) == 0
Šie testai praeina. Mūsų funkcija atrodo teisinga, remiantis šiais pavyzdžiais. Bet dabar parašykime savybėmis grįstą testą su Hypothesis. Kokia yra pagrindinė absoliučios vertės funkcijos savybė? Rezultatas niekada neturėtų būti neigiamas.
# test_my_math.py (savybėmis grįstas su Hypothesis) from hypothesis import given from hypothesis import strategies as st from my_math import custom_abs @given(st.integers()) def test_abs_property_is_non_negative(x): """Savybė: bet kurio sveikojo skaičiaus absoliuti vertė visada yra >= 0.""" assert custom_abs(x) >= 0
Išsiaiškinkime, kas čia vyksta:
from hypothesis import given, strategies as st
: Importuojame reikiamus komponentus.given
yra dekoratorius, kuris paverčia įprastą testo funkciją savybėmis grįstu testu.strategies
yra modulis, kuriame randame savo duomenų generatorius.@given(st.integers())
: Tai yra testo esmė.@given
dekoratorius nurodo Hypothesis paleisti šią testo funkciją kelis kartus. Kiekvienam paleidimui ji sugeneruos vertę naudodama pateiktą strategiją,st.integers()
, ir perduos ją kaip argumentąx
mūsų testo funkcijai.assert custom_abs(x) >= 0
: Tai yra mūsų savybė. Mes tvirtiname, kad bet kokiam sveikam skaičiuix
, kurį Hypothesis sugalvos, mūsų funkcijos rezultatas turi būti didesnis arba lygus nuliui.
Kai paleisite tai su pytest
, jis greičiausiai praeis daugeliui verčių. Hypothesis bandys 0, -1, 1, didelius teigiamus skaičius, didelius neigiamus skaičius ir daugiau. Mūsų paprasta funkcija teisingai apdoroja visus šiuos atvejus. Dabar pabandykime kitą strategiją, kad pamatytume, ar galime rasti silpną vietą.
# Išbandykime su slankiojo kablelio skaičiais @given(st.floats()) def test_abs_floats_property(x): assert custom_abs(x) >= 0
Jei paleisite tai, Hypothesis greitai ras nesėkmingą atvejį!
Falsifying example: test_abs_floats_property(x=nan) ... assert custom_abs(nan) >= 0 AssertionError: assert nan >= 0
Hypothesis atrado, kad mūsų funkcija, gavusi float('nan')
(angl. Not a Number – ne skaičius), grąžina nan
. Tvirtinimas nan >= 0
yra klaidingas. Ką tik radome subtilią klaidą, kurios tikriausiai nebūtume sugalvoję patikrinti rankiniu būdu. Galėtume pataisyti savo funkciją, kad ji apdorotų šį atvejį, galbūt išmesdama ValueError
arba grąžindama konkrečią vertę.
Dar geriau, kas jei klaida būtų su labai specifiniu slankiojo kablelio skaičiumi? Hypothesis sutraukiklis būtų paėmęs didelį, sudėtingą nesėkmingą skaičių ir sumažinęs jį iki paprasčiausios įmanomos versijos, kuri vis dar sukelia klaidą.
Strategijų galia: testavimo duomenų kūrimas
Strategijos yra Hypothesis širdis. Tai yra receptai duomenims generuoti. Bibliotekoje yra platus integruotų strategijų spektras, ir jūs galite jas derinti ir pritaikyti, kad sugeneruotumėte beveik bet kokią duomenų struktūrą, kokią tik galite įsivaizduoti.
Dažniausios integruotos strategijos
- Skaitinės:
st.integers(min_value=0, max_value=1000)
: Generuoja sveikuosius skaičius, pasirinktinai nurodytame diapazone.st.floats(min_value=0.0, max_value=1.0, allow_nan=False, allow_infinity=False)
: Generuoja slankiojo kablelio skaičius, su smulkiagrūdžiu specialių verčių valdymu.st.fractions()
,st.decimals()
- Tekstinės:
st.text(min_size=1, max_size=50)
: Generuoja tam tikro ilgio unikodo eilutes.st.text(alphabet='abcdef0123456789')
: Generuoja eilutes iš konkretaus simbolių rinkinio (pvz., šešioliktainiams kodams).st.characters()
: Generuoja pavienius simbolius.
- Kolekcijos:
st.lists(st.integers(), min_size=1)
: Generuoja sąrašus, kurių kiekvienas elementas yra sveikasis skaičius. Atkreipkite dėmesį, kaip mes perduodame kitą strategiją kaip argumentą! Tai vadinama kompozicija.st.tuples(st.text(), st.booleans())
: Generuoja kortezus (tuples) su fiksuota struktūra.st.sets(st.integers())
st.dictionaries(keys=st.text(), values=st.integers())
: Generuoja žodynus su nurodytais raktų ir reikšmių tipais.
- Laiko:
st.dates()
,st.times()
,st.datetimes()
,st.timedeltas()
. Jos gali būti pritaikytos laiko juostoms.
- Įvairios:
st.booleans()
: GeneruojaTrue
arbaFalse
.st.just('constant_value')
: Visada generuoja tą pačią vienintelę vertę. Naudinga kuriant sudėtingas strategijas.st.one_of(st.integers(), st.text())
: Generuoja vertę iš vienos iš pateiktų strategijų.st.none()
: Generuoja tikNone
.
Strategijų derinimas ir transformavimas
Tikroji Hypothesis galia slypi jos gebėjime kurti sudėtingas strategijas iš paprastesnių.
Naudojant .map()
.map()
metodas leidžia paimti vertę iš vienos strategijos ir paversti ją kažkuo kitu. Tai puikiai tinka kuriant jūsų individualių klasių objektus.
# Paprasta duomenų klasė from dataclasses import dataclass @dataclass class User: user_id: int username: str # Strategija, skirta User objektams generuoti user_strategy = st.builds( User, user_id=st.integers(min_value=1), username=st.text(min_size=3, alphabet='abcdefghijklmnopqrstuvwxyz') ) @given(user=user_strategy) def test_user_creation(user): assert isinstance(user, User) assert user.user_id > 0 assert user.username.isalpha()
Naudojant .filter()
ir assume()
Kartais reikia atmesti tam tikras sugeneruotas vertes. Pavyzdžiui, jums gali prireikti sveikųjų skaičių sąrašo, kurio suma nėra nulis. Galėtumėte naudoti .filter()
:
st.lists(st.integers()).filter(lambda x: sum(x) != 0)
Tačiau .filter()
naudojimas gali būti neefektyvus. Jei sąlyga dažnai yra klaidinga, Hypothesis gali ilgai bandyti sugeneruoti tinkamą pavyzdį. Geresnis požiūris dažnai yra naudoti assume()
savo testo funkcijos viduje:
from hypothesis import assume @given(st.lists(st.integers())) def test_something_with_non_zero_sum_list(numbers): assume(sum(numbers) != 0) # ... jūsų testo logika čia ...
assume()
sako Hypothesis: „Jei ši sąlyga netenkinama, tiesiog atmesk šį pavyzdį ir bandyk naują.“ Tai tiesesnis ir dažnai našesnis būdas apriboti testavimo duomenis.
Naudojant st.composite()
Tikrai sudėtingam duomenų generavimui, kai viena sugeneruota vertė priklauso nuo kitos, st.composite()
yra jums reikalingas įrankis. Jis leidžia parašyti funkciją, kuri kaip argumentą priima specialią draw
funkciją, kurią galite naudoti norėdami žingsnis po žingsnio gauti vertes iš kitų strategijų.
Klasikinis pavyzdys – sąrašo ir jam tinkančio indekso generavimas.
@st.composite def list_and_index(draw): # Pirmiausia, sugeneruojame ne tuščią sąrašą my_list = draw(st.lists(st.integers(), min_size=1)) # Tada sugeneruojame indeksą, kuris garantuotai tinka tam sąrašui index = draw(st.integers(min_value=0, max_value=len(my_list) - 1)) return (my_list, index) @given(data=list_and_index()) def test_list_access(data): my_list, index = data # Ši prieiga yra garantuotai saugi dėl to, kaip sukūrėme strategiją element = my_list[index] assert element is not None # Paprastas tvirtinimas
Hypothesis veikiant: realaus pasaulio scenarijai
Pritaikykime šias koncepcijas realesnėms problemoms, su kuriomis programinės įrangos kūrėjai susiduria kasdien.
1 scenarijus: Duomenų serializavimo funkcijos testavimas
Įsivaizduokite funkciją, kuri serializuoja vartotojo profilį (žodyną) į URL saugią eilutę, ir kitą, kuri ją deserializuoja. Pagrindinė savybė yra ta, kad procesas turi būti visiškai grįžtamas.
import json import base64 def serialize_profile(data: dict) -> str: """Serializuoja žodyną į URL saugią base64 eilutę.""" json_string = json.dumps(data) return base64.urlsafe_b64encode(json_string.encode('utf-8')).decode('utf-8') def deserialize_profile(encoded_str: str) -> dict: """Deserializuoja eilutę atgal į žodyną.""" json_string = base64.urlsafe_b64decode(encoded_str.encode('utf-8')).decode('utf-8') return json.loads(json_string) # Dabar testas # Mums reikia strategijos, kuri generuoja su JSON suderinamus žodynus json_dictionaries = st.dictionaries( keys=st.text(), values=st.recursive(st.none() | st.booleans() | st.floats(allow_nan=False) | st.text(), lambda children: st.lists(children) | st.dictionaries(st.text(), children), max_leaves=10) ) @given(profile=json_dictionaries) def test_serialization_roundtrip(profile): """Savybė: iškodavus užkoduotą profilį, turi būti gautas originalus profilis.""" encoded = serialize_profile(profile) decoded = deserialize_profile(encoded) assert profile == decoded
Šis vienintelis testas išbandys mūsų funkcijas su didžiule duomenų įvairove: tuščiais žodynais, žodynais su įdėtais sąrašais, žodynais su unikodo simboliais, žodynais su keistais raktais ir dar daugiau. Tai daug kruopščiau nei parašyti kelis rankinius pavyzdžius.
2 scenarijus: Rūšiavimo algoritmo testavimas
Grįžkime prie mūsų rūšiavimo pavyzdžio. Štai kaip testuotumėte savybes, kurias apibrėžėme anksčiau.
from collections import Counter def my_buggy_sort(numbers): # Įterpkime subtilią klaidą: ji pašalina dublikatus return sorted(list(set(numbers))) @given(st.lists(st.integers())) def test_sorting_properties(numbers): sorted_list = my_buggy_sort(numbers) # 1 savybė: išvestis yra surūšiuota for i in range(len(sorted_list) - 1): assert sorted_list[i] <= sorted_list[i+1] # 2 savybė: elementai yra tie patys (tai ras klaidą) assert Counter(numbers) == Counter(sorted_list) # 3 savybė: funkcija yra idempotentiška assert my_buggy_sort(sorted_list) == sorted_list
Kai paleisite šį testą, Hypothesis greitai ras nesėkmingą pavyzdį 2-ajai savybei, pavyzdžiui, numbers=[0, 0]
. Mūsų funkcija grąžina [0]
, o Counter([0, 0])
nėra lygus Counter([0])
. Sutraukiklis užtikrins, kad nesėkmingas pavyzdys būtų kuo paprastesnis, todėl klaidos priežastis iš karto taps akivaizdi.
3 scenarijus: Būsenos testavimas
Objektams su vidine būsena, kuri laikui bėgant keičiasi (pvz., duomenų bazės ryšys, pirkinių krepšelis ar podėlis (cache)), rasti klaidas gali būti neįtikėtinai sunku. Norint sukelti klaidą, gali prireikti specifinės operacijų sekos. Hypothesis būtent tam tikslui pateikia `RuleBasedStateMachine`.
Įsivaizduokite paprastą API atmintyje esančiai raktų ir reikšmių saugyklai:
class SimpleKeyValueStore: def __init__(self): self._data = {} def set(self, key, value): self._data[key] = value def get(self, key): return self._data.get(key) def delete(self, key): if key in self._data: del self._data[key] def size(self): return len(self._data)
Galime modeliuoti jos elgseną ir testuoti su būsenos mašina:
from hypothesis.stateful import RuleBasedStateMachine, rule, Bundle class KeyValueStoreMachine(RuleBasedStateMachine): def __init__(self): super().__init__() self.model = {} self.sut = SimpleKeyValueStore() # Bundle() naudojamas duomenims perduoti tarp taisyklių keys = Bundle('keys') @rule(target=keys, key=st.text(), value=st.integers()) def set_key(self, key, value): self.model[key] = value self.sut.set(key, value) return key @rule(key=keys) def delete_key(self, key): del self.model[key] self.sut.delete(key) @rule(key=st.text()) def get_key(self, key): model_val = self.model.get(key) sut_val = self.sut.get(key) assert model_val == sut_val @rule() def check_size(self): assert len(self.model) == self.sut.size() # Norint paleisti testą, tiesiog sukuriama poklasė iš mašinos ir unittest.TestCase # Naudojant pytest, galima tiesiog priskirti testą mašinos klasei TestKeyValueStore = KeyValueStoreMachine.TestCase
Hypothesis dabar vykdys atsitiktines `set_key`, `delete_key`, `get_key` ir `check_size` operacijų sekas, nenuilstamai bandydama rasti seką, kuri sukeltų vieno iš tvirtinimų nesėkmę. Ji patikrins, ar teisingai elgiamasi gaunant ištrintą raktą, ar dydis yra nuoseklus po kelių nustatymų ir ištrynimų, ir daugelį kitų scenarijų, kurių galbūt nesugalvotumėte testuoti rankiniu būdu.
Geroji praktika ir patarimai pažengusiems
- Pavyzdžių duomenų bazė: Hypothesis yra išmani. Radusi klaidą, ji išsaugo nesėkmingą pavyzdį vietiniame kataloge (
.hypothesis/
). Kitą kartą paleidus testus, ji pirmiausia atkurs tą nesėkmingą pavyzdį, suteikdama jums tiesioginį grįžtamąjį ryšį, kad klaida vis dar yra. Kai ją ištaisysite, pavyzdys nebebus atkuriamas. - Testo vykdymo valdymas su
@settings
: Galite valdyti daugelį testo vykdymo aspektų naudodami@settings
dekoratorių. Galite padidinti pavyzdžių skaičių, nustatyti terminą, kiek laiko gali trukti vienas pavyzdys (kad pagautumėte begalines kilpas), ir išjungti tam tikrus sveikumo patikrinimus.@settings(max_examples=500, deadline=1000) # Vykdyti 500 pavyzdžių, 1 sekundės terminas @given(...) ...
- Klaidų atkartojimas: Kiekvienas Hypothesis paleidimas atspausdina pradinę vertę (angl. seed), pvz.,
@reproduce_failure('version', 'seed')
. Jei CI serveris randa klaidą, kurios negalite atkartoti vietoje, galite naudoti šį dekoratorių su pateikta pradine verte, kad priverstumėte Hypothesis vykdyti lygiai tą pačią pavyzdžių seką. - Integracija su CI/CD: Hypothesis puikiai tinka bet kokiam nuolatinės integracijos konvejeriui. Jos gebėjimas rasti neaiškias klaidas, kol jos nepasiekė produkcijos, daro ją neįkainojamu saugumo tinklu.
Mąstymo pokytis: galvojimas savybėmis
Priimti Hypothesis yra daugiau nei tiesiog išmokti naują biblioteką; tai reiškia priimti naują mąstymo būdą apie savo kodo teisingumą. Užuot klausę „Kokius įvesties duomenis turėčiau testuoti?“, pradedate klausti „Kokios yra universalios tiesos apie šį kodą?“
Štai keletas klausimų, padėsiančių jums nustatyti savybes:
- Ar egzistuoja atvirkštinė operacija? (pvz., serializavimas/deserializavimas, šifravimas/dešifravimas, glaudinimas/išskleidimas). Savybė yra ta, kad atlikus operaciją ir jos atvirkštinę operaciją, turėtų būti gauta pradinė įvestis.
- Ar operacija yra idempotentiška? (pvz.,
abs(abs(x)) == abs(x)
). Pritaikant funkciją daugiau nei vieną kartą, turėtų būti gautas tas pats rezultatas kaip ir pritaikius ją vieną kartą. - Ar egzistuoja kitas, paprastesnis būdas apskaičiuoti tą patį rezultatą? Galite testuoti, ar jūsų sudėtinga, optimizuota funkcija duoda tą patį rezultatą kaip ir paprasta, akivaizdžiai teisinga versija (pvz., testuojant savo išmanųjį rūšiavimą su Python integruota
sorted()
funkcija). - Kas visada turi būti tiesa apie išvestį? (pvz., `find_prime_factors` funkcijos išvestyje turėtų būti tik pirminiai skaičiai, o jų sandauga turėtų būti lygi įvesčiai).
- Kaip keičiasi būsena? (Būsenos testavimui) Kokie invariantai turi būti išlaikyti po bet kokios teisingos operacijos? (pvz., Prekių skaičius pirkinių krepšelyje niekada negali būti neigiamas).
Išvada: naujas pasitikėjimo lygis
Savybėmis grįstas testavimas su Hypothesis nepakeičia pavyzdžiais pagrįsto testavimo. Jums vis dar reikia konkrečių, ranka rašytų testų kritinei verslo logikai ir gerai suprantamiems reikalavimams (pvz., „Vartotojas iš šalies X turi matyti kainą Y“).
Hypothesis suteikia galingą, automatizuotą būdą ištirti jūsų kodo elgseną ir apsisaugoti nuo nenumatytų kraštutinių atvejų. Jis veikia kaip nenuilstantis partneris, generuojantis tūkstančius testų, kurie yra įvairesni ir klastingesni, nei bet kuris žmogus galėtų realiai parašyti. Apibrėždami pagrindines savo kodo savybes, sukuriate tvirtą specifikaciją, pagal kurią Hypothesis gali testuoti, suteikdama jums naują pasitikėjimo savo programine įranga lygį.
Kitą kartą, kai rašysite funkciją, skirkite akimirką pagalvoti ne tik apie pavyzdžius. Paklauskite savęs: „Kokios yra taisyklės? Kas visada turi būti tiesa?“ Tada leiskite Hypothesis atlikti sunkų darbą bandant jas sulaužyti. Nustebsite, ką jis ras, o jūsų kodas nuo to taps tik geresnis.